/**
 * Copyright (c) 2006-2011 LOVE Development Team
 * 
 * This software is provided 'as-is', without any express or implied
 * warranty.  In no event will the authors be held liable for any damages
 * arising from the use of this software.
 * 
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 * 
 * 1. The origin of this software must not be misrepresented; you must not
 *    claim that you wrote the original software. If you use this software
 *    in a product, an acknowledgment in the product documentation would be
 *    appreciated but is not required.
 * 2. Altered source versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 * 3. This notice may not be removed or altered from any source distribution.
 **/

#include "Audio.h"
#include <common/delay.h>

#include <sound/Decoder.h>

namespace love
{
    namespace audio
    {
        namespace openal
        {
            Audio::PoolThread::PoolThread(Pool* pool)
            : pool(pool), finish(false) {
                
            }
            
            void Audio::PoolThread::main()
            {
                while(true) {
                    {
                        thread::Lock lock(mutex);
                        if (finish) {
                            return;
                        }
                    }
                    
                    pool->update();
                    delay(5);
                }
            }
            
            void Audio::PoolThread::setFinish()
            {
                thread::Lock lock(mutex);
                finish = true;
            }
            
            
            Audio::Audio()
            {
                // Passing zero for default device.
                device = alcOpenDevice(0);
                
                if(device == 0)
                    throw love::Exception("Could not open device.");
                
                context = alcCreateContext(device, 0);
                
                if(context == 0)
                    throw love::Exception("Could not create context.");
                
                alcMakeContextCurrent(context);
                
                if(alcGetError(device) != ALC_NO_ERROR)
                    throw love::Exception("Could not make context current.");
                
                /*std::string captureName(alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER));
                 const ALCchar * devices = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
                 while (*devices) {
                 std::string device(devices);
                 devices += device.size() + 1;
                 if (device.find("Mic") != std::string::npos || device.find("mic") != std::string::npos) {
                 captureName = device;
                 }
                 }
                 
                 capture = alcCaptureOpenDevice(captureName.c_str(), 8000, AL_FORMAT_MONO16, 262144); // about 32 seconds
                 
                 if (!capture) {
                 // We're not going to prevent LOVE from running without a microphone, but we should warn, at least
                 std::cerr << "Warning, couldn't open capture device! No audio input!" << std::endl;
                 }*/
                
                // pool must be allocated after AL context.
                pool = new Pool();
                
                poolThread = new PoolThread(pool);
                poolThread->start();
            }
            
            Audio::~Audio()
            {
                poolThread->setFinish();
                poolThread->wait();
                
                delete poolThread;
                delete pool;
                
                alcMakeContextCurrent(0);
                alcDestroyContext(context);
                //if (capture) alcCaptureCloseDevice(capture);
                alcCloseDevice(device);
            }
            
            
            const char * Audio::getName() const
            {
                return "love.audio.openal";
            }
            
            love::audio::Source * Audio::newSource(love::sound::Decoder * decoder)
            {
                return new Source(pool, decoder);
            }
            
            love::audio::Source * Audio::newSource(love::sound::SoundData * soundData)
            {
                return new Source(pool, soundData);
            }
            
            int Audio::getNumSources() const
            {
                return pool->getNumSources();
            }
            
            int Audio::getMaxSources() const
            {
                return pool->getMaxSources();
            }
            
            void Audio::play(love::audio::Source * source)
            {
                source->play();
            }
            
            void Audio::stop(love::audio::Source * source)
            {
                source->stop();
            }
            
            void Audio::stop()
            {
                pool->stop();
            }
            
            void Audio::pause(love::audio::Source * source)
            {
                source->pause();
            }
            
            void Audio::pause()
            {
                pool->pause();
            }
            
            void Audio::resume(love::audio::Source * source)
            {
                source->resume();
            }
            
            void Audio::resume()
            {
                pool->resume();
            }
            
            void Audio::rewind(love::audio::Source * source)
            {
                source->rewind();
            }
            
            void Audio::rewind()
            {
                pool->rewind();
            }
            
            void Audio::setVolume(float volume)
            {
                alListenerf(AL_GAIN, volume);
            }
            
            float Audio::getVolume() const
            {
                ALfloat volume;
                alGetListenerf(AL_GAIN, &volume);
                return volume;
            }
            
            void Audio::getPosition(float * v) const
            {
                alGetListenerfv(AL_POSITION, v);
            }
            
            void Audio::setPosition(float * v)
            {
                alListenerfv(AL_POSITION, v);
            }
            
            void Audio::getOrientation(float * v) const
            {
                alGetListenerfv(AL_ORIENTATION, v);
            }
            
            void Audio::setOrientation(float * v)
            {
                alListenerfv(AL_ORIENTATION, v);
            }
            
            void Audio::getVelocity(float * v) const
            {
                alGetListenerfv(AL_VELOCITY, v);
            }
            
            void Audio::setVelocity(float * v)
            {
                alListenerfv(AL_VELOCITY, v);
            }
            
            void Audio::record()
            {
                if (!canRecord()) return;
                alcCaptureStart(capture);
            }
            
            love::sound::SoundData * Audio::getRecordedData()
            {
                if (!canRecord()) return NULL;
                int samplerate = 8000;
                ALCint samples;
                alcGetIntegerv(capture, ALC_CAPTURE_SAMPLES, 4, &samples);
                void * data = malloc(samples * (2/sizeof(char)));
                alcCaptureSamples(capture, data, samples);
                love::sound::SoundData * sd = new love::sound::SoundData(data, samples, samplerate, 16, 1);
                free(data);
                return sd;
            }
            
            love::sound::SoundData * Audio::stopRecording(bool returnData)
            {
                if (!canRecord()) return NULL;
                love::sound::SoundData * sd = NULL;
                if (returnData) {
                    sd = getRecordedData();
                }
                alcCaptureStop(capture);
                return sd;
            }
            
            bool Audio::canRecord()
            {
                return (capture != NULL);
            }
            
        } // openal
    } // audio
} // love
